package edu.osu.slate.relatedness.swwr.setup.graph;

import java.io.*;
import java.util.*;

import edu.osu.slate.relatedness.Configuration;
import edu.osu.slate.relatedness.swwr.data.ConvertIDToTitle;
import edu.osu.slate.relatedness.swwr.data.ConvertTitleToID;
import edu.osu.slate.relatedness.swwr.data.graph.IDIDRedirect;
import edu.osu.slate.relatedness.swwr.data.graph.IDVertexTranslation;

import it.unimi.dsi.fastutil.objects.*;
import it.unimi.dsi.fastutil.ints.*;

/**
 * This program generates the redirect file for a wiki structure.
 * <p>
 * The inputs to this program are two Wikipedia files (redirect.sql and page.sql) and the name of a .vid file (previously generated using {@link wwr.init.graph.CreateValidIDFile}.
 * <p>
 * The output of this program is a .rdr file containing two integer array objects (from[]) (to[]).  This file is used to construct a {@link IDIDRedirect.data.RedirectList} object.
 * <p>
 * <i>File Names:</i>
 * <UL>
 * <LI>[redirectfile] Input redirect.sql file
 * <LI>[pagefile] Input page.sql file
 * <LI>[vidfile] Input .vid file
 * <LI>[outputfile] Output .rdr file
 * </UL>
 * 
 * Requires the <a href="http://fastutil.dsi.unimi.it/">fastutil</a> jar be installed in the classpath.
 * @author weale
 * @version 1.0;alpha
 *
 */
public class CreateRedirectFiles {

  /* Name of the input file generated by Wikipedia (redirect.sql) */
  private static String redirectFileName;
  
  /* Name of the Valid ID file generated by CreateIDToVertexFile (.vid) */
  private static String vidFileName;

  /* Name of the input file (.tid) */
  private static String tidFileName;

  /* Name of the output file (.rdr) */
  private static String outputFileName;

  private static void setFiles()
  {
    String binaryDir = Configuration.baseDir + "/" +
                       Configuration.binaryDir + "/" +
                       Configuration.type + "/" +
                       Configuration.date + "/";
    
    String sourceDir = Configuration.baseDir + "/" +
                       Configuration.sourceDir + "/" +
                       Configuration.type + "/" +
                       Configuration.date + "/";
    
    redirectFileName = sourceDir +
                       Configuration.type + "-" +
                       Configuration.date + "-" +
                       "redirect.sql";
    
    vidFileName = binaryDir +
                  Configuration.type + "-" +
                  Configuration.date + "-" +
                  Configuration.graph + ".vid";

    tidFileName = binaryDir +
                  Configuration.type + "-" +
                  Configuration.date + "-" +
                  Configuration.graph + ".tid";
    
    outputFileName = binaryDir +
                     Configuration.type + "-" +
                     Configuration.date + "-" +
                     Configuration.graph + ".rdr";
  }
  
 /**
  * Runs the program.
  * 
  * @param args Command-line parameters
  */
  public static void main(String [] args) throws Exception {

    if(args.length == 1)
    {
      Configuration.parseConfigurationFile(args[0]);
    }
    else
    {
      Configuration.parseConfigurationFile("/scratch/weale/data/config/enwiktionary/CreateMappings.xml");
    }
    
    setFiles();
    
    /* STEP 1
     * 
     * Open files.
     */
    System.out.println("Reading ID-Vertex Translation File");
    IDVertexTranslation vids = null;
    try
    {
      ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(vidFileName));
      vids = (IDVertexTranslation) objIn.readObject();
      objIn.close();
    }
    catch(Exception e)
    {
      System.out.println("Problem with file: " + vidFileName);
      System.exit(1);
    }

    System.out.println("Reading Title-ID Table.");
    ConvertTitleToID title2ID = null;
    ConvertIDToTitle ID2Title = null;
    try
    {
      ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(tidFileName));
      title2ID = (ConvertTitleToID) objIn.readObject();
      ID2Title = (ConvertIDToTitle) objIn.readObject();
      objIn.close();
    }
    catch(Exception e)
    {
      System.out.println("Problem with file: " + tidFileName);
      System.exit(1);
    }
    
    /* STEP 2
     * 
     * Set up initial redirects.
     * 
     * All redirects are as-is. That is, the resulting IDs
     * may or may not be valid for our given graph.
     */
    System.out.println("Populating Initial Redirects");
    Int2IntAVLTreeMap IDToIDRedirect = new Int2IntAVLTreeMap();
    Scanner in = new Scanner(new FileReader(redirectFileName));

    String str = in.nextLine();
    while(str.indexOf("INSERT INTO") == -1)
    {
      str = in.nextLine();
    }

    while(str != null && !str.trim().equals(""))
    {
      str = str.substring(str.indexOf("(")+1, str.length()-3);

      // Split the String into the page information
      String [] arr = str.split("\\),\\(");
      for(int i = 0; i < arr.length; i++)
      {
        String [] info = arr[i].split(",");

        // Check if the information is in the correct format
        if(info.length >= 3) {

          // Extract page, namespace and redirect information
          int fromID = Integer.parseInt(info[0]);
          String namespace = info[1];

          String title = info[2];
          for(int j = 3; j < info.length; j++)
          {
            title = title + "," + info[j];
          }
          
          title = title.substring(1, title.length()-1);

          // Valid namespace and valid title
          if( namespace.equals("0") && title2ID.isLookupTitle(title))
          {
            if(vids.isValidWikiID(fromID))
            { // 'from' ID is already a vertex ID
              System.err.println("Vertex ID found");
            }
            else
            { // get destination ID
              int destID = title2ID.getID(title);
              IDToIDRedirect.put(fromID, destID);
            }
          }//end: if(namespace==0)
        }//end: if(info.length)
      }//end: for(i)

      str = in.nextLine();
    }//end: while()
    in.close();

    /* STEP 3
     * 
     * Given ID to ID redirects, follow and converge the destination IDs
     * to a valid graph vertex (if possible).
     */
    System.out.println("Converging Redirects");
    boolean converged = false;
    Int2IntAVLTreeMap Redirect = (Int2IntAVLTreeMap) IDToIDRedirect.clone();
    
    while(!converged)
    {
      converged = true;

      ObjectSortedSet<Map.Entry<Integer, Integer>> oss = IDToIDRedirect.entrySet();
      ObjectBidirectionalIterator<Map.Entry<Integer, Integer>> it = oss.iterator();

      while(it.hasNext())
      {
        Map.Entry<Integer, Integer> me = it.next();
        int fromID = me.getKey();
        int toID = me.getValue();
        
        if( !vids.isValidWikiID(toID) )
        { // Destination ID is not a vertex graph
          converged = false;

          if(fromID == toID)
          {
            // Self-redirect
            Redirect.remove(fromID);
          }

          else if(IDToIDRedirect.get(toID) == IDToIDRedirect.defaultReturnValue())
          {
            // Redirect is not listed, or not in-scope of the graph
            Redirect.remove(fromID);
          }
          else
          {
            // Redirect is valid, update list w/ new redirect value.
            Redirect.put(fromID, IDToIDRedirect.get(toID));
          }
        }
      }//end: while(it)
      
      IDToIDRedirect = (Int2IntAVLTreeMap) Redirect.clone();
    }//end: while(!converged)

    /* STEP 6
     * 
     * Create output arrays
     */
    System.out.println("Writing Redirect Arrays");
    int[] fromID = new int[IDToIDRedirect.size()];
    int[] redirID = new int[IDToIDRedirect.size()];
    int i = 0;

    ObjectSortedSet<Map.Entry<Integer, Integer>> oss = IDToIDRedirect.entrySet();
    ObjectBidirectionalIterator<Map.Entry<Integer, Integer>> it = oss.iterator();
    while(it.hasNext())
    {
      Map.Entry<Integer, Integer> me = it.next();
      fromID[i] = me.getKey();
      redirID[i] = me.getValue();
      i++;
    }

    /* STEP 7
     * 
     * Write objects to output file.
     */
    System.out.println("Writing Redirect File");
    try
    {
      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(outputFileName));
      IDIDRedirect obj = new IDIDRedirect(fromID, redirID);
      out.writeObject(obj);
      out.close();
    }
    catch(Exception e)
    {
      System.err.println("Problem writing to file: " + outputFileName);
      e.printStackTrace();
      System.exit(1);
    }
  }
}
